//
//  FBtCorer.m
//  JwPack
//
//  Created by 陈警卫 on 2025/7/21.
//

#import "FBtCorer.h"

@interface FBtCorer ()<CBCentralManagerDelegate, CBPeripheralDelegate>

/// 中心设备
@property (nonatomic, strong) CBCentralManager *centralManager;
/// 外设数组
/// 数组中仅只有 (FBtPeriStateScaning/FBtPeriStateConnected/FBtPeriStateNotifyed) 状态的FBtPeriModel
@property (nonatomic, strong) NSMutableArray<FBtPeriModel *> *periModels;
/// 查信号计时器
@property (nonatomic, strong) NSTimer *timerRssi;

@end

@implementation FBtCorer

#pragma mark - init

/// 初始化
- (instancetype)initWithConfig:(FBtConfig *)config {
    self = [super init];
    if (self) {
        self.config = config;
        [self setupData];
    }
    return self;
}

- (void)setupData {
    self.periModels = [NSMutableArray array];
    
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    [options addEntriesFromDictionary:@{CBCentralManagerOptionShowPowerAlertKey: [NSNumber numberWithBool:self.config.showPowerAlertKey]}];
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(0, 0) options:options];
}

#pragma mark - handle

/// 添加扫描自动连接外设
- (void)addScanConnectPeriModel:(FBtPeriModel *)periModel {
    if (!periModel.connectUuid && !periModel.connectName) {
        return;
    }
    periModel.state = FBtPeriStateScaning;
    FBtPeriModel *exitPeriModel = [self periModelsWithPeri:periModel.peri];
    if (exitPeriModel.state == FBtPeriStateDefault) {
        [self addPeriModels:periModel];
    } else if (exitPeriModel.state == FBtPeriStateScaning) {
        return;
    } else if (exitPeriModel.state == FBtPeriStateConnected) {
        return;
    } else if (exitPeriModel.state == FBtPeriStateNotifyed) {
        return;
    }
}

/// 初始化过滤
- (void)initFilt {
    for (FBtPeriModel *periModel in self.periModels) {
        if (periModel.state == FBtPeriStateScaning) {
            if (periModel.connectName) {
                [self.config.advFiltNames addObject:periModel.connectName];
            }
            if (periModel.connectUuid) {
                [self.config.advPeriFiltUUIDs addObject:[CBUUID UUIDWithString:periModel.connectUuid]];
            }
        }
    }
}


#pragma mark - scan

/// 扫描
- (void)scan {
    [self stopScan];
    [self initFilt];
    
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    [options addEntriesFromDictionary:@{CBCentralManagerScanOptionAllowDuplicatesKey: [NSNumber numberWithBool:self.config.allowDuplicatesKey]}];
    NSArray *scanUUIDs = [self.config.scanUUIDs allObjects];
    if (scanUUIDs.count <= 0 ) {
        scanUUIDs = nil;
    }
    [self.centralManager scanForPeripheralsWithServices:scanUUIDs options:options];
}

/// 停止扫描
- (void)stopScan {
    [self.centralManager stopScan];
}

#pragma mark - connect

/// 连接
- (void)connectPeriModel:(FBtPeriModel *)periModel {
    if (periModel.peri) {
        periModel.state = FBtPeriStateConnecting;
        [self addPeriModels:periModel];
        [self.centralManager connectPeripheral:periModel.peri options:nil];
    }
}

/// 断开连接
- (void)disconnectPeriModel:(FBtPeriModel *)periModel {
    if (periModel.peri) {
        [self.centralManager cancelPeripheralConnection:periModel.peri];
    }
}

- (void)disconnectUserPeriModel:(FBtPeriModel *)periModel {
    periModel.state = FBtPeriStateUserDisconnect;
    [self disconnectPeriModel:periModel];
}

/// 通过外设UUID恢复
- (CBPeripheral *)retrievePeripheralsWithUuid:(NSString *)uuid {
    if (uuid) {
        NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:uuid];
        NSArray *peris = [self.centralManager retrievePeripheralsWithIdentifiers:@[UUID]];
        if (peris.count > 0) {
            return peris.firstObject;
        }
    }
    return nil;
}

#pragma mark - notify

/// 自动订阅外设
- (void)notifyPeriModel:(FBtPeriModel *)periModel comple:(void(^)(NSError *error))comple {
    if (periModel.peri) {
        // 直接连接
        [self connectPeriModel:periModel];
    } else {
        if (!periModel.connectUuid && !periModel.connectName) {
            kFBtLog(@"notifyPeriModel--%@", @"connectUuid/connectName 都为nil");
            NSError *error = [NSError errorWithDomain:@"" code:0 userInfo:nil];
            if (comple) {
                comple(error);
            }
            return;
        }
        BOOL isScan = YES;
        if (periModel.retrieve && periModel.connectUuid) {
            CBPeripheral *peri = [self retrievePeripheralsWithUuid:periModel.connectUuid];
            if (peri) {
                [self connectPeriModel:periModel];
                isScan = NO;
            }
        }
        if (isScan) {
            periModel.state = FBtPeriStateScaning;
            [self addPeriModels:periModel];
            [self scan];
        }
    }
    if (periModel.timeout > 0) {
        periModel.didTimerAction = comple;
        [periModel startTimer];
    }
}

#pragma mark - CBCentralManagerDelegate

// 状态回调
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    CBManagerState state = central.state;
    if (state == CBManagerStateUnknown) {
        self.state = FBtStateUnsupported;
    } else if (state == CBManagerStateResetting) {
        self.state = FBtStateUnsupported;
    } else if (state == CBManagerStateUnsupported) {
        self.state = FBtStateUnsupported;
    } else if (state == CBManagerStateUnauthorized) {
        self.state = FBtStateUnauthorized;
    } else if (state == CBManagerStatePoweredOff) {
        self.state = FBtStatePoweredOff;
    } else if (state == CBManagerStatePoweredOn) {
        self.state = FBtStatePoweredOn;
    }
}

// 扫描回调
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
    
    // 无名称过滤
    if (peripheral.name.length <= 0) {
        return;
    }
    
    //kFBtLog(@"扫描外设--%@", peripheral);
    
    BOOL isContain = NO;
    // 外设广播服务UUID过滤
    NSArray *advUUIDs = [advertisementData objectForKey:CBAdvertisementDataServiceUUIDsKey];
    if (self.config.advServiceFiltUUIDs.count > 0) {
        for (CBUUID *uuid in advUUIDs) {
            if ([self.config.advServiceFiltUUIDs containsObject:uuid]) {
                isContain = YES;
                break;
            }
        }
    }
    // 外设名称过滤
    if (self.config.advFiltNames.count > 0) {
        for (NSString *advFiltName in self.config.advFiltNames) {
            if ([peripheral.name containsString:advFiltName]) {
                isContain = YES;
                break;
            }
        }
    }
    // 外设UUID过滤
    if (self.config.advPeriFiltUUIDs.count > 0) {
        for (CBUUID *uuid in self.config.advPeriFiltUUIDs) {
            if ([peripheral.identifier isEqual:uuid]) {
                isContain = YES;
                break;
            }
        }
    }
    
    
    if (isContain) {
        kFBtLog(@"过滤扫描外设--%@--%@", peripheral, advertisementData);
        
        FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
        if (!periModel) {
            periModel = [[FBtPeriModel alloc] init];
            periModel.peri = peripheral;
        }
        periModel.peri = peripheral;
        periModel.advDict = advertisementData;
        periModel.rssi = RSSI;
        
        // 扫描连接
        if ([self isContainsPeriModel:periModel state:(FBtPeriStateScaning)]) {
            [self connectPeriModel:periModel];
        }
        
        periModel.state = FBtPeriStateScaned;
        if (self.didScanedPeri) {
            self.didScanedPeri(periModel);
        }
    }
}

// 连接回调
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    kFBtLog(@"连接成功--%@", peripheral);
    
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    periModel.state = FBtPeriStateConnected;
    NSArray *serviceUUIDs = periModel.serviceUUIDs;
    if (serviceUUIDs.count <= 0) {
        serviceUUIDs = nil;
    }
    
    peripheral.delegate = self;
    [peripheral discoverServices:serviceUUIDs];
    
    [self addPeriModels:periModel];
    
    if (self.didConncetedPeri) {
        self.didConncetedPeri(periModel);
    }
    
    if (![self isContainsPeriModelWithState:(FBtPeriStateScaning)]) {
        // 停止扫描
        [self stopScan];
    }
}

// 连接失败回调
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    kFBtLog(@"连接失败--%@--%@", peripheral, error);
    
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    if (!periModel) {
        periModel = [[FBtPeriModel alloc] init];
        periModel.peri = peripheral;
    }
    periModel.state = FBtPeriStateConnectFail;
    [self removePeriModels:periModel];
    
    if (self.didConncetFailPeri) {
        self.didConncetFailPeri(periModel);
    }
}

// 断开连接回调
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    kFBtLog(@"断开连接--%@--%@", peripheral, error);
    
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    if (!periModel) {
        periModel = [[FBtPeriModel alloc] init];
        periModel.peri = peripheral;
    }
    periModel.state = FBtPeriStateDisconnect;
    [self removePeriModels:periModel];
    
    if (self.didDisconncetPeri) {
        self.didDisconncetPeri(periModel);
    }
}

#pragma mark - CBPeripheralDelegate

// 发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    kFBtLog(@"发现服务--%@--%@", peripheral, peripheral.services);
    
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    
    NSMutableArray *charaUuids = [NSMutableArray array];
    [charaUuids addObjectsFromArray:periModel.writeUUIDs];
    [charaUuids addObjectsFromArray:periModel.notifUUIDs];
    if (charaUuids.count <= 0) {
        charaUuids = nil;
    }
    
    BOOL isSuccess = NO;
    for (CBService *service in peripheral.services) {
        if ([periModel.serviceUUIDs containsObject:service.UUID]) {
            isSuccess = YES;
            [peripheral discoverCharacteristics:charaUuids forService:service];
        }
    }
    if (!isSuccess || error) {
        [self disconnectPeriModel:periModel];
        kFBtLog(@"发现服务失败--%@--%@", peripheral, error);
    }
}

// 发现特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    kFBtLog(@"发现特征--%@--%@--%@", peripheral, service, service.characteristics);
    
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    
    BOOL isSuccess_write = NO;
    BOOL isSuccess_notif = NO;
    for (CBCharacteristic *chara in service.characteristics) {
        if ([periModel.writeUUIDs containsObject:chara.UUID]) {
            kFBtLog(@"发现写入特征--%@", chara);
            isSuccess_write = YES;
            periModel.writeChara = chara;
        }
        if ([periModel.notifUUIDs containsObject:chara.UUID]) {
            kFBtLog(@"发现订阅特征--%@", chara);
            isSuccess_notif = YES;
            periModel.notifChara = chara;
            [peripheral setNotifyValue:YES forCharacteristic:chara];
        }
    }
    if (periModel.writeUUIDs.count > 0) {
        if (!isSuccess_write) {
            kFBtLog(@"发现写入特征失败--%@--%@", service, error);
            [self disconnectPeriModel:periModel];
        }
    }
    if (periModel.notifUUIDs.count > 0) {
        if (!isSuccess_notif) {
            kFBtLog(@"发现订阅特征失败--%@--%@", service, error);
            [self disconnectPeriModel:periModel];
        }
    }
    if (error) {
        kFBtLog(@"发现特征失败--%@--%@", service, error);
        [self disconnectPeriModel:periModel];
    }
}

// 订阅状态回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    kFBtLog(@"订阅回调--%@--%@", peripheral, characteristic);
    
    BOOL isSuccess = NO;
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    if (characteristic.isNotifying) {
        if ([periModel.notifUUIDs containsObject:characteristic.UUID]) {
            kFBtLog(@"订阅成功--%@--%@", peripheral, characteristic);
            isSuccess = YES;
            
            periModel.state = FBtPeriStateNotifyed;
            periModel.peri = peripheral;
            periModel.notifChara = characteristic;
            [self addPeriModels:periModel];
            
            if (self.didNotifedPeri) {
                self.didNotifedPeri(periModel);
            }
        }
    }
    if (!isSuccess || error) {
        kFBtLog(@"订阅失败---%@---%@", characteristic, error);
        [self disconnectPeriModel:periModel];
    }
}

// 订阅数据回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    kFBtLog(@"订阅数据回调--%@--%@", peripheral, characteristic);
    
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    
    if ([periModel.notifUUIDs containsObject:characteristic.UUID]) {
        NSData *data = characteristic.value;
        if (self.didNotifedValue) {
            self.didNotifedValue(periModel, data);
        }
    }
}

// 信号回调
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error {
    FBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.rssi = RSSI;
    if (self.didRssiPeri) {
        self.didRssiPeri(periModel);
    }
}

#pragma mark - handle model

// 获取缓存外设
- (FBtPeriModel *)periModelsWithPeri:(CBPeripheral *)peri {
    NSString *uuid = peri.identifier.UUIDString;
    FBtPeriModel *periModel = nil;
    for (FBtPeriModel *model in self.periModels) {
        if ([model.periUuid isEqualToString:uuid] ||
            [model.connectName isEqualToString:peri.name] ||
            [model.connectUuid isEqualToString:peri.identifier.UUIDString]) {
            periModel = model;
            break;
        }
    }
    return periModel;
}

// 删除缓存外设
- (void)removePeriModels:(FBtPeriModel *)periModel {
    NSString *uuid = periModel.peri.identifier.UUIDString;
    [self.periModels enumerateObjectsWithOptions:(NSEnumerationReverse) usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        FBtPeriModel *model = (FBtPeriModel *)obj;
        if ([model.periUuid isEqualToString:uuid]) {
            [self.periModels removeObject:obj];
        }
    }];
}

// 添加缓存外设
- (void)addPeriModels:(FBtPeriModel *)periModel {
    [self removePeriModels:periModel];
    [self.periModels addObject:periModel];
}

#pragma mark - set

- (void)setState:(FBtState)state {
    if (_state == FBtStatePoweredOff && state == FBtStatePoweredOn) {
        // 自动连接
    }
    _state = state;
    if (state == FBtStatePoweredOff) {
        // 蓝牙关闭
    }
    if (self.didState) {
        self.didState(state);
    }
}

#pragma mark - tool

- (BOOL)isNotifedUuid:(NSString *)uuid {
    FBtPeriModel *periModel = [[FBtPeriModel alloc] init];
    periModel.connectUuid = uuid;
    return [self isNotifedPeriModel:periModel];
}

- (BOOL)isNotifedName:(NSString *)name {
    FBtPeriModel *periModel = [[FBtPeriModel alloc] init];
    periModel.connectName = name;
    return [self isNotifedPeriModel:periModel];
}

- (BOOL)isNotifedPeriModel:(FBtPeriModel *)periModel {
    return [self isContainsPeriModel:periModel state:(FBtPeriStateNotifyed)];
}

// state: FBtPeriStateDefault 为包含判断
- (BOOL)isContainsPeriModel:(FBtPeriModel *)periModel state:(FBtPeriState)state {
    BOOL isContained = NO;
    for (FBtPeriModel *model in self.periModels) {
        if ([model.periUuid isEqualToString:periModel.periUuid] ||
            [model.periUuid isEqualToString:periModel.connectUuid] ||
            [model.periName isEqualToString:periModel.periName] ||
            [model.periName isEqualToString:periModel.connectName] ||
            [model.connectUuid isEqualToString:periModel.periUuid] ||
            [model.connectUuid isEqualToString:periModel.connectUuid] ||
            [model.connectName isEqualToString:periModel.periName] ||
            [model.connectName isEqualToString:periModel.connectName]) {
            if (state == FBtPeriStateDefault) {
                isContained = YES;
                break;
            } else {
                if (state == periModel.state) {
                    isContained = YES;
                    break;
                }
            }
        }
    }
    return isContained;
}

- (BOOL)isContainsPeriModelWithState:(FBtPeriState)state {
    BOOL isContained = NO;
    for (FBtPeriModel *model in self.periModels) {
        if (state == model.state) {
            isContained = YES;
            break;
        }
    }
    return isContained;
}


/// 获取已订阅的外设
- (NSArray<FBtPeriModel *> *)notifedPeriModel {
    NSMutableArray *periModels = [NSMutableArray array];
    for (FBtPeriModel *model in self.periModels) {
        if (model.state == FBtPeriStateNotifyed) {
            [periModels addObject:model];
        }
    }
    return periModels;
}

@end
